{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "43b954e3",
   "metadata": {},
   "source": [
    "## Test in Python\n",
    "\n",
    "Testing in Python is a huge topic and can come with a lot of complexity, but it doesn’t need to be hard. You can get started creating simple tests for your application in a few easy steps and then build on it from there.\n",
    "\n",
    "### Automated vs. Manual Testing\n",
    "* The good news is, you’ve probably already created a test without realizing it. Remember when you ran your application and used it for the first time? Did you __check the features and experiment using them__? That’s known as __exploratory testing__ and is a form of __manual testing__.\n",
    "\n",
    "* __Exploratory testing is a form of testing that is done without a plan__. In an exploratory test, you’re just exploring the application.\n",
    "\n",
    "* To have a complete set of manual tests, all you need to do is make a list of all the features your application has, the different types of input it can accept, and the expected results. Now, every time you make a change to your code, __you need to go through every single item on that list and check it.__\n",
    "\n",
    "That doesn’t sound like much fun, does it?\n",
    "\n",
    "\n",
    "* This is where __automated testing__ comes in. Automated testing is the execution of your test plan (the parts of your application you want to test, the order in which you want to test them, and the expected responses) __by a script instead of a human__.\n",
    "\n",
    "* Python already comes with a set of tools and libraries to help you create automated tests for your application. We’ll explore those tools and libraries in this tutorial."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e3cc8d36",
   "metadata": {},
   "source": [
    "### Unit Tests vs. Integration Tests\n",
    "\n",
    "* Think of how you might test the lights on a car. You would turn on the lights (known as the test step) and go outside the car or ask a friend to check that the lights are on (known as the test assertion). Testing multiple components is known as integration testing.\n",
    "\n",
    "* Think of all the things that need to work correctly in order for a simple task to give the right result. These components are like the parts to your application, all of those classes, functions, and modules you’ve written.\n",
    "\n",
    "* __A major challenge with integration testing is when an integration test doesn’t give the right result. It’s very hard to diagnose the issue without being able to isolate which part of the system is failing__. If the lights didn’t turn on, then maybe the bulbs are broken. Is the battery dead? What about the alternator? Is the car’s computer failing?\n",
    "\n",
    "* If you have a fancy modern car, it will tell you when your light bulbs have gone. It does this using a form of unit test.\n",
    "\n",
    "* A unit test is a smaller test, one that checks that a single component operates in the right way. A unit test helps you to isolate what is broken in your application and fix it faster.\n",
    "\n",
    "### Unit Tests\n",
    "\n",
    "* To write a unit test for the built-in function sum(), you would check the output of sum() against a known output.\n",
    "\n",
    "For example, here’s how you check that the sum() of the numbers (1, 2, 3) equals 6:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "397dc0c1",
   "metadata": {},
   "outputs": [],
   "source": [
    " assert sum([1, 2, 3]) == 6, \"Should be 6\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ccd6e174",
   "metadata": {},
   "source": [
    "This will not output anything on the REPL because the values are correct.\n",
    "\n",
    "If the result from sum() is incorrect, this will fail with an AssertionError and the message \"Should be 6\". Try an assertion statement again with the wrong values to see an AssertionError:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c14e853d",
   "metadata": {},
   "outputs": [],
   "source": [
    "assert sum([1, 1, 1]) == 6, \"Should be 6\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "855d873b",
   "metadata": {},
   "outputs": [],
   "source": [
    "def test_sum():\n",
    "    assert sum([1, 2, 3]) == 6, \"Should be 6\"\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    test_sum()\n",
    "    print(\"Everything passed\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cb2a805f",
   "metadata": {},
   "source": [
    "In Python, sum() accepts any iterable as its first argument. You tested with a list. Now test with a tuple as well."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "16840f2d",
   "metadata": {},
   "outputs": [],
   "source": [
    "def test_sum():\n",
    "    assert sum([1, 2, 3]) == 6, \"Should be 6\"\n",
    "\n",
    "def test_sum_tuple():\n",
    "    assert sum((1, 2, 2)) == 6, \"Should be 6\"\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    test_sum()\n",
    "    test_sum_tuple()\n",
    "    print(\"Everything passed\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cdf939f6",
   "metadata": {},
   "source": [
    "### Choosing a Test Runner\n",
    "* There are many test runners available for Python. The one built into the Python standard library is called unittest.  The principles of unittest are easily portable to other frameworks. The three most popular test runners are:\n",
    "\n",
    "    * unittest\n",
    "    * nose or nose2\n",
    "    * pytest\n",
    "    \n",
    "Choosing the best test runner for your requirements and level of experience is important."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6884b70a",
   "metadata": {},
   "source": [
    "### unittest\n",
    "* __unittest__ has been built into the Python standard library since version 2.1. You’ll probably see it in commercial Python applications and open-source projects.\n",
    "\n",
    "* __unittest__ contains both a testing framework and a test runner. unittest has some important requirements for writing and executing tests.\n",
    "\n",
    "unittest requires that:\n",
    "\n",
    "* You put your tests into __classes__ as methods\n",
    "* You use a series of special __assertion methods_ in the unittest.TestCase class instead of the built-in assert statement\n",
    "* To convert the earlier example to a unittest test case, you would have to:\n",
    "\n",
    "    * Import unittest from the standard library\n",
    "    * Create a class called TestSum that inherits from the TestCase class\n",
    "    * Convert the test functions into methods by adding self as the first argument\n",
    "    * Change the assertions to use the self.assertEqual() method on the TestCase class\n",
    "    * Change the command-line entry point to call unittest.main()\n",
    "    * Follow those steps by creating a new file test_sum_unittest.py with the following code:\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "96321cb7",
   "metadata": {},
   "outputs": [],
   "source": [
    "import unittest\n",
    "\n",
    "\n",
    "class TestSum(unittest.TestCase):\n",
    "\n",
    "    def test_sum(self):\n",
    "        self.assertEqual(sum([1, 2, 3]), 6, \"Should be 6\")\n",
    "\n",
    "    def test_sum_tuple(self):\n",
    "        self.assertEqual(sum((1, 2, 2)), 6, \"Should be 6\")\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    unittest.main(argv=['first-arg-is-ignored'],exit=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7a055f1f",
   "metadata": {},
   "outputs": [],
   "source": [
    "import unittest\n",
    "#需要测试的函数(摄氏度转华氏度)\n",
    "def c2f(c):\n",
    "    return c * 1.8 + 32\n",
    "\n",
    "class Testdef(unittest.TestCase):\n",
    "    def test_c2f(self):#必须以test开头否则不会执行\n",
    "        #用不同的输入\n",
    "        c1 = 0\n",
    "        c2 = 100\n",
    "        c3 = 1.5\n",
    "        c4 = -1.5\n",
    "        f1 = c2f(c1)\n",
    "        f2 = c2f(c2)\n",
    "        f3 = c2f(c3)\n",
    "        f4 = c2f(c4)\n",
    "        #编写断言\n",
    "        self.assertEqual(f1,32)#判断f1和预期32 是否相等\n",
    "        self.assertEqual(f2,212)\n",
    "        self.assertEqual(f3,34.7)\n",
    "        self.assertEqual(f4,5456)\n",
    "# if __name__ == '__main__':\n",
    "#     unittest.main()  \n",
    "#以上面方法执行，若报错AttributeError: module '__main__' has no attribute '\n",
    "#可采用下面的方法\n",
    "if __name__ == '__main__':\n",
    "        unittest.main(argv=['first-arg-is-ignored'],exit=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a6affe0e",
   "metadata": {},
   "outputs": [],
   "source": [
    "import unittest\n",
    "def div(a,b):\n",
    "    return a/b\n",
    "\n",
    "class Myfirstunittest(unittest.TestCase):\n",
    "\n",
    "\n",
    "    def test_testcase(self):\n",
    "        self.assertEquals(div(1,1),1/1)\n",
    "\n",
    "    def test_testcase2(self):\n",
    "        self.assertEquals(div(3,4),3/4)\n",
    "\n",
    "    def test_testcase3(self):\n",
    "        print ('3/0')\n",
    "        #执行代码运行到div这个方法时，如果a参数是3，b参数是0时，就是抛出异常\n",
    "        self.assertRaises(ZeroDivisionError,div(3,0) )\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    unittest.main(argv=['first-arg-is-ignored'],exit=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b59c69b5",
   "metadata": {},
   "source": [
    "### 小结 \n",
    "\n",
    "单元测试可以有效地测试某个程序模块的行为，是未来重构代码的信心保证。\n",
    "\n",
    "单元测试的测试用例要覆盖常用的输入组合、边界条件和异常。\n",
    "\n",
    "单元测试代码要非常简单，如果测试代码太复杂，那么测试代码本身就可能有bug。\n",
    "\n",
    "单元测试通过了并不意味着程序就没有bug了，但是不通过程序肯定有bug。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c1ea54e9",
   "metadata": {},
   "source": [
    "# 文档测试 (Doc Test)\n",
    "\n",
    "\n",
    "\n",
    "**文档测试（doctest）** 模块可以直接提取注释中的代码并执行测试\n",
    "\n",
    "doctest严格按照Python交互式命令行的输入和输出来判断测试结果是否正确。只有测试异常的时候，可以用...表示中间一大段烦人的输出。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e8532469",
   "metadata": {},
   "source": [
    "### Example"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "6ec0ce0f",
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"\n",
    "This is the \"example\" module.\n",
    "\n",
    "The example module supplies one function, factorial().  For example,\n",
    "\n",
    ">>> factorial(5)\n",
    "120\n",
    "\"\"\"\n",
    "\n",
    "def factorial(n):\n",
    "    \"\"\"Return the factorial of n, an exact integer >= 0.\n",
    "\n",
    "    >>> [factorial(n) for n in range(6)]\n",
    "    [1, 1, 2, 6, 24, 120]\n",
    "    >>> factorial(30)\n",
    "    265252859812191058636308480000000\n",
    "    >>> factorial(-1)\n",
    "    Traceback (most recent call last):\n",
    "        ...\n",
    "    ValueError: n must be >= 0\n",
    "\n",
    "    Factorials of floats are OK, but the float must be an exact integer:\n",
    "    >>> factorial(30.1)\n",
    "    Traceback (most recent call last):\n",
    "        ...\n",
    "    ValueError: n must be exact integer\n",
    "    >>> factorial(30.0)\n",
    "    265252859812191058636308480000000\n",
    "\n",
    "    It must also not be ridiculously large:\n",
    "    >>> factorial(1e100)\n",
    "    Traceback (most recent call last):\n",
    "        ...\n",
    "    OverflowError: n too large\n",
    "    \"\"\"\n",
    "\n",
    "    import math\n",
    "    if not n >= 0:\n",
    "        raise ValueError(\"n must be >= 0\")\n",
    "    if math.floor(n) != n:\n",
    "        raise ValueError(\"n must be exact integer\")\n",
    "    if n+1 == n:  # catch a value like 1e300\n",
    "        raise OverflowError(\"n too large\")\n",
    "    result = 1\n",
    "    factor = 2\n",
    "    while factor <= n:\n",
    "        result *= factor\n",
    "        factor += 1\n",
    "    return result\n",
    "\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    import doctest\n",
    "    doctest.testmod()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c5fdc9b8",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "8076f6cf",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "**********************************************************************\n",
      "File \"__main__\", line 11, in __main__.abs\n",
      "Failed example:\n",
      "    abs(0)\n",
      "Expected:\n",
      "    3\n",
      "Got:\n",
      "    0\n",
      "**********************************************************************\n",
      "1 items had failures:\n",
      "   1 of   3 in __main__.abs\n",
      "***Test Failed*** 1 failures.\n"
     ]
    }
   ],
   "source": [
    "def abs(n):\n",
    "    '''\n",
    "    Function to get absolute value of number.\n",
    "    \n",
    "    Example:\n",
    "    \n",
    "    >>> abs(1)\n",
    "    1\n",
    "    >>> abs(-1)\n",
    "    1\n",
    "    >>> abs(0)\n",
    "    3\n",
    "    '''\n",
    "    return n if n >= 0 else (-n)\n",
    "if __name__=='__main__':\n",
    "    import doctest\n",
    "    doctest.testmod()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "82b3c056",
   "metadata": {},
   "source": [
    "什么输出也没有，这说明我们编写的doctest运行都是正确的。如果程序有问题，运行就会报错，如上面所示\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
